package org.fastsyncer.connector.ldap;

import java.util.List;
import java.util.Map;

import javax.naming.directory.Attributes;
import javax.naming.directory.SearchControls;

import org.apache.commons.lang.StringUtils;
import org.fastsyncer.common.constant.ConnectorConstant;
import org.fastsyncer.common.entity.BaseInfo;
import org.fastsyncer.common.entity.LdapConfig;
import org.fastsyncer.connector.util.LdapUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ldap.AuthenticationException;
import org.springframework.ldap.CommunicationException;
import org.springframework.ldap.NamingException;
import org.springframework.ldap.core.AttributesMapper;
import org.springframework.ldap.core.DirContextAdapter;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.ldap.filter.AndFilter;
import org.springframework.ldap.filter.EqualsFilter;
import org.springframework.ldap.filter.Filter;
import org.springframework.ldap.filter.GreaterThanOrEqualsFilter;
import org.springframework.ldap.filter.LessThanOrEqualsFilter;
import org.springframework.ldap.filter.NotFilter;
import org.springframework.ldap.filter.OrFilter;

public final class LdapConnector implements Ldap {

    private static final Logger LOG = LoggerFactory.getLogger(LdapConnector.class);

    private static volatile LdapConnector instance = null;

    private LdapConnector(){
    }
    
    public static LdapConnector getInstance() {
        if (null == instance) {
            synchronized (LdapConnector.class) {
                if (null == instance) {
                    instance = new LdapConnector();
                }
            }
        }
        return instance;
    }

    @Override
    public boolean isAlive(Object config) {
        LdapConfig cfg = (LdapConfig) config;
        try {
            LdapUtil.getLdapTemplate(cfg);
            return true;
        } catch (Exception e) {
            LOG.error("Failed to connect:" + cfg.getUrl(), e.getMessage());
        }
        return false;
    }

    @Override
    public LdapTemplate getLdapTemplate(LdapConfig config) throws AuthenticationException, CommunicationException, javax.naming.NamingException {
        return LdapUtil.getLdapTemplate(config);
    }

    @Override
    public void close(DirContextAdapter ctx) {
        LdapUtil.closeContext(ctx);
    }

    @Override
    public BaseInfo getBaseInfoConnector(Object config) {
        LdapConfig cfg = (LdapConfig) config;
        LdapTemplate ldapTemplate;
        try {
            ldapTemplate = LdapUtil.getLdapTemplate(cfg);
        } catch (Exception e) {
            LOG.error("", e.getLocalizedMessage());
            return null;
        }

        // 且
        AndFilter filter = new AndFilter();
        List<String> list = cfg.getExtendObjects();
        for (String objClz : list) {
            filter.and(new EqualsFilter("objectclass", objClz));
        }

        BaseInfo connector = LdapUtil.getDataSourceConnector(ldapTemplate, filter.encode());
        return connector;
    }

    private int count;

    @SuppressWarnings({ "rawtypes", "unchecked" })
    public int getRowCount(LdapTemplate ldapTemplate, String filter) throws javax.naming.NamingException {
        count = 0;
        // 转换器
        AttributesMapper mapper = new AttributesMapper() {
            public Object mapFromAttributes(Attributes attrs) throws NamingException, javax.naming.NamingException {
                count++;
                return null;
            }
        };

        SearchControls controls = new SearchControls();
        controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
        // 查询返回的字段
        controls.setReturningAttributes(new String[] { "objectclass" });
        ldapTemplate.search("", filter, controls, mapper);
        return count;
    }

    /**
     * @Title: getFilter 
     * @Description: 获取查询条件
     * @param extendObjects 继承对象
     * @param query 过滤的字段，包括and/or条件
     * @return: String
     */
    public String getFilter(List<String> extendObjects, Map<String, List<Map<String, String>>> query) {
        // 解析继承对象
        AndFilter filter = new AndFilter();
        for (String obj : extendObjects) {
            filter.and(new EqualsFilter("objectclass", obj));
        }

        // 解析同步的属性
        OrFilter orFilter = new OrFilter();
        if (query != null && !query.isEmpty()) {
            // 拼接并且
            List<Map<String, String>> addList = query.get(ConnectorConstant.OPERTION_QUERY_AND);
            this.getOperFilter(ConnectorConstant.OPERTION_QUERY_AND, orFilter, addList);

            // 拼接或者
            List<Map<String, String>> orList = query.get(ConnectorConstant.OPERTION_QUERY_OR);
            this.getOperFilter(ConnectorConstant.OPERTION_QUERY_OR, orFilter, orList);
        }

        // 添加过滤属性
        String f = orFilter.encode();
        if (!StringUtils.isBlank(f)) {
            filter.and(orFilter);
        }

        String c = filter.encode();
        return StringUtils.isBlank(c) ? "(objectclass=*)" : c;
    }

    private void getOperFilter(String operator, OrFilter filter, List<Map<String, String>> list) {
        if (list == null || list.isEmpty()) {
            return;
        }
        int size = list.size();
        switch (operator) {
        case ConnectorConstant.OPERTION_QUERY_AND:
            AndFilter filterAdd = new AndFilter();
            for (int i = 0; i < size; i++) {
                Map<String, String> c = list.get(i);
                String name = c.get("name");
                String oper = c.get("operator");
                String value = c.get("value");
                // 获取查询条件
                Filter f = this.getFilterByOpr(oper, name, value);
                filterAdd.and(f);
            }
            filter.or(filterAdd);
            break;
        case ConnectorConstant.OPERTION_QUERY_OR:
            OrFilter filterOr = new OrFilter();
            for (int i = 0; i < size; i++) {
                Map<String, String> c = list.get(i);
                String name = c.get("name");
                String oper = c.get("operator");
                String value = c.get("value");
                // 获取查询条件
                Filter f = this.getFilterByOpr(oper, name, value);
                filterOr.or(f);
            }
            filter.or(filterOr);
            break;
        default:
            break;
        }
    }

    private Filter getFilterByOpr(String oper, String name, String value) {
        Filter f = null;
        switch (oper) {
        case "equal":
            f = new EqualsFilter(name, value);
            break;
        case "notEqual":
            f = new NotFilter(new EqualsFilter(name, value));
            break;
        case "gtAndEqual":
            f = new GreaterThanOrEqualsFilter(name, value);
            break;
        case "ltAndEqual":
            f = new LessThanOrEqualsFilter(name, value);
            break;
        default:
            // 默认相等
            f = new EqualsFilter(name, value);
            break;
        }
        return f;
    }

}
